package main

import (
	"context"
	"reflect"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type MongoD interface {
	CollectionName() string
}

type Mdb[T MongoD] struct {
	database         string          // 数据库命
	Uri              string          // uri
	ctx              context.Context // 使用的ctx
	mongoClient      *mongo.Client
	optionsInterface interface{} // options对象
	filter           bson.M      // 查找时候的筛选
}

// Mdb构造者接口
type MdbBuild interface {
	SetBase(ctx context.Context, m *mongo.Client, database string)
}

// Mdb构造者
type MdbBuilder[T MongoD] struct {
	mdb *Mdb[T]
}

func (b *MdbBuilder[T]) SetBase(ctx context.Context, m *mongo.Client, database string) {
	if b.mdb == nil {
		b.mdb = new(Mdb[T])
	}
	b.mdb.mongoClient = m
	b.mdb.ctx = ctx
	b.mdb.database = database
}

// mongo执行对象
func GMongo[T MongoD](ctx context.Context, m *mongo.Client, database string) *Mdb[T] {
	gmb := new(MdbBuilder[T])
	gmb.SetBase(ctx, m, database)
	return gmb.mdb
}

// 设置查询Options
func (m *Mdb[T]) SetOptions(ops interface{}) *Mdb[T] {
	m.optionsInterface = ops
	return m
}

// 等有必要了再自己定义解析结构
func transformBson[T MongoD](in T) ([]byte, error) {
	return bson.Marshal(in)
}

// fillZero 是否填充零值， true:填充零值
func transformBMap[T MongoD](in T, fillZero bool) (map[string]interface{}, error) {
	res := map[string]interface{}{}
	typ := reflect.TypeOf(in)
	val := reflect.ValueOf(in)

	for typ.Kind() == reflect.Ptr {
		val = val.Elem()
		typ = typ.Elem()
	}

	for i := 0; i < typ.NumField(); i++ {
		typFd := typ.Field(i)
		valFd := val.Field(i)
		tagValue := typFd.Tag.Get("bson")
		if fillZero {
			if len(tagValue) > 0 {
				res[tagValue] = valFd.Interface()
			}

		} else {
			if len(tagValue) > 0 && !valFd.IsZero() {
				res[tagValue] = valFd.Interface()
			}
		}
	}

	return res, nil
}

// 转化筛选结构--将对应的输入类型转化为bson.M。（目前仅支持单层结构体）
// 用于查询辅助筛选字段
func (m *Mdb[T]) MFilter(in T) *Mdb[T] {
	res, _ := transformBMap[T](in, false)
	m.filter = res

	return m
}

/* Insert 相关 */
func (m *Mdb[T]) InsertOne(in T) (interface{}, error) {
	bys, err := transformBson(in)
	if nil != err {
		return nil, err
	}
	var insertOneOps *options.InsertOneOptions
	var ok bool
	collection := m.mongoClient.Database(m.database).Collection(in.CollectionName())
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		insertOneOps, ok = m.optionsInterface.(*options.InsertOneOptions)
		if !ok {
			insertOneOps = nil
		}
	}

	res, err := collection.InsertOne(m.ctx, bys, insertOneOps)
	if nil != err {
		return nil, err
	}
	return res.InsertedID, nil
}

func (m *Mdb[T]) InsertMany(in []T) ([]interface{}, error) {
	val := []interface{}{}
	for i := range in {
		val = append(val, in[i])
	}
	var typ T
	collection := m.mongoClient.Database(m.database).Collection(typ.CollectionName())
	var insertOManyOps *options.InsertManyOptions
	var ok bool
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		insertOManyOps, ok = m.optionsInterface.(*options.InsertManyOptions)
		if !ok {
			insertOManyOps = nil
		}
	}

	res, err := collection.InsertMany(m.ctx, val, insertOManyOps)
	if nil != err {
		return nil, err
	}
	return res.InsertedIDs, nil
}

/* Find 相关 */
func (m *Mdb[T]) FindOne() (out T, err error) {

	var findOneOps *options.FindOneOptions
	var ok bool
	collection := m.mongoClient.Database(m.database).Collection(out.CollectionName())
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		findOneOps, ok = m.optionsInterface.(*options.FindOneOptions)
		if !ok {
			findOneOps = nil
		}
	}

	res := collection.FindOne(m.ctx, m.filter, findOneOps)
	if err = res.Decode(&out); err != nil {
		return
	}
	return
}

// warn:只使用了.All方法，针对嵌套结构体有点问题
func (m *Mdb[T]) FindMany() (out []T, err error) {

	var typ T
	var findOps *options.FindOptions
	var ok bool
	collection := m.mongoClient.Database(m.database).Collection(typ.CollectionName())
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		findOps, ok = m.optionsInterface.(*options.FindOptions)
		if !ok {
			findOps = nil
		}
	}

	res, err := collection.Find(m.ctx, m.filter, findOps)
	if nil != err {
		return nil, err
	}
	if err = res.All(m.ctx, &out); err != nil {
		return
	}
	return
}

func (m *Mdb[T]) UpdateOne(in T) (interface{}, error) {

	var Ops *options.UpdateOptions
	var ok bool
	collection := m.mongoClient.Database(m.database).Collection(in.CollectionName())
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		Ops, ok = m.optionsInterface.(*options.UpdateOptions)
		if !ok {
			Ops = nil
		}
	}

	inBson, _ := transformBMap[T](in, true)

	res, err := collection.UpdateOne(m.ctx, m.filter, bson.M{"$set": inBson}, Ops)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// func (m *Mdb[T]) UpdateMany(in []T) (interface{}, error) {

// 	var typ T
// 	var Ops *options.UpdateOptions
// 	var ok bool
// 	collection := m.mongoClient.Database(m.database).Collection(typ.CollectionName())
// 	if m.optionsInterface != nil {
// 		// 类型断言校验选项的结构
// 		Ops, ok = m.optionsInterface.(*options.UpdateOptions)
// 		if !ok {
// 			Ops = nil
// 		}
// 	}

// 	res, err := collection.UpdateMany(m.ctx, m.filter, Ops)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return res, nil
// }

func (m *Mdb[T]) UpSertOne(in T) (interface{}, error) {

	var Ops *options.UpdateOptions
	var ok bool
	collection := m.mongoClient.Database(m.database).Collection(in.CollectionName())
	if m.optionsInterface != nil {
		// 类型断言校验选项的结构
		Ops, ok = m.optionsInterface.(*options.UpdateOptions)
		if !ok {
			Ops = nil
		}
	}

	inBson, _ := transformBMap[T](in, true)

	res, err := collection.UpdateOne(m.ctx, m.filter, bson.M{"$set": inBson}, options.Update().SetUpsert(true), Ops)
	if err != nil {
		return nil, err
	}
	return res, nil
}
